// +FHDR------------------------------------------------------------
//                 Copyright (c) 2022 .
//                       ALL RIGHTS RESERVED
// -----------------------------------------------------------------
// Filename      : spt_transaction.sv
// Author        : 
// Created On    : 2022-08-25 14:42
// Last Modified : 
// -----------------------------------------------------------------
// Description:
//
//
// -FHDR------------------------------------------------------------

`ifndef __SPT_TRANSACTION_SV__
`define __SPT_TRANSACTION_SV__

class spt_transaction extends uvm_sequence_item;

    rand int      cycle_post;

    rand bit      head_err;
    rand bit      tail_err;
    rand bit[1:0] payload_mode;//0:glitch, 1:short, 2:normal, 3:long

    rand int      payload_len;
    rand int      pkt_len;
    
    rand bit[15:0]header;
    rand bit[15:0]tailer;
    rand bit[15:0]payload_q[$];

    bit[15:0]     pkt_data[];
    
    `uvm_object_utils_begin(spt_transaction)
        `uvm_field_int(head_err, UVM_ALL_ON)
        `uvm_field_int(tail_err, UVM_ALL_ON)
        `uvm_field_int(payload_mode, UVM_ALL_ON)
        `uvm_field_int(payload_len, UVM_ALL_ON)
        `uvm_field_int(pkt_len, UVM_ALL_ON)
        `uvm_field_queue_int(payload_q, UVM_ALL_ON)
        `uvm_field_int(header, UVM_ALL_ON)
        `uvm_field_int(tailer, UVM_ALL_ON)
        //`uvm_field_array_int(pkt_data, UVM_ALL_ON)
    `uvm_object_utils_end

    extern constraint cycle_post_cons;
    extern constraint head_err_cons;
    extern constraint tail_err_cons;
    extern constraint payload_mode_cons;
    extern constraint pkt_len_cons;
    extern constraint payload_len_cons;
    extern constraint payload_q_size_cons;
    extern constraint header_cons;
    
    extern function new(string name = "spt_transaction");
    extern virtual function spt_transaction clone();
    extern virtual function spt_transaction spt_proc();
	extern virtual function void pack();
	extern virtual function void unpack();
    extern function bit[15:0] tailer_cal(bit[15:0] payload[$]);
	
endclass: spt_transaction

constraint spt_transaction::cycle_post_cons{
    cycle_post dist {1:/30, [1:10]:/30, [10:20]:/30};
}

constraint spt_transaction::head_err_cons{
    head_err dist {0:/90, 1:/10};
}

constraint spt_transaction::tail_err_cons{
    tail_err dist {0:/90, 1:/10};
}

constraint spt_transaction::payload_mode_cons{
    payload_mode dist {0:/10, 1:/10, 2:/70, 3:/10};
}

constraint spt_transaction::pkt_len_cons{
    payload_mode == 0 -> pkt_len dist {[1:2]:/10};      //glitch;
    payload_mode == 1 -> pkt_len dist {[3:21]:/10};     //short
    payload_mode == 2 -> pkt_len dist {[22:602]:/10};   //normal
    payload_mode == 3 -> pkt_len dist {[603:1000]:/10}; //long
}

constraint spt_transaction::payload_len_cons{
    pkt_len <= 2 -> payload_len == 0;
    pkt_len >  2 -> payload_len == pkt_len - 2;
}

constraint spt_transaction::payload_q_size_cons{
    payload_q.size() == payload_len;
}

constraint spt_transaction::header_cons{
    head_err == 1 -> header != 16'h55d5;
    head_err != 1 -> header == 16'h55d5;
}

function spt_transaction::new(string name = "spt_transaction");
	super.new(name);
endfunction

function spt_transaction spt_transaction::clone();
    spt_transaction to = new();
    
    to.head_err = this.head_err;
    to.tail_err = this.tail_err;
    to.payload_mode = this.payload_mode;

    to.payload_len = this.payload_len;
    to.pkt_len = this.pkt_len;

    to.header = this.header;
    to.tailer = this.tailer;
    foreach(this.payload_q[i]) to.payload_q[i] = this.payload_q[i];
    foreach(this.pkt_data[i]) to.pkt_data[i] = this.pkt_data[i];
    
    return to;
endfunction: clone

function bit[15:0] spt_transaction::tailer_cal(bit[15:0] payload[$]);
	bit[16:0] sum_17;                      
	bit[15:0] sum_16;        
	
	foreach(payload[i]) begin
		sum_17 = payload[i] + sum_16;
		sum_16 = sum_17[16] + sum_17[15:0];
		if(sum_16 == 16'hFFFF)begin 
			tailer_cal = 16'hFFFF;
		end
		else begin
			tailer_cal = ~sum_16;
		end
        //$display("payload[%0d], payload='h%0h, sum_17='h%0h, sum_16='h%0h", i, payload[i], sum_17, sum_16);
	end
    //$display("tailer_cal = 'h%0h", tailer_cal);
    
endfunction: tailer_cal

function void spt_transaction::pack();

    bit[15:0]tailer = tailer_cal(this.payload_q);

    this.pkt_data = new[this.pkt_len];
    this.pkt_data[0] = this.header;

    if(tail_err)
        std::randomize(this.tailer) with {this.tailer != tailer;};
    else
        this.tailer = tailer;
    this.pkt_data[pkt_len-1] = this.tailer;

    foreach(this.payload_q[i])begin
        this.pkt_data[i+1] = this.payload_q[i];
    end

    if(this.pkt_len == 1) this.header = this.tailer;
    if(this.header != 16'h55d5) this.head_err = 1;
    
endfunction: pack

function void spt_transaction::unpack();
    
    bit[15:0]tailer;

    this.pkt_len = this.pkt_data.size();
    if(this.pkt_len <= 2) 
        this.payload_len = 0;
    else 
        this.payload_len = this.pkt_len - 2;

    this.header = this.pkt_data[0];
    this.tailer = this.pkt_data[this.pkt_len-1];
    for(int i=0; i<this.payload_len; i+=1)begin
        this.payload_q[i] = this.pkt_data[i+1];
    end
    
    tailer = tailer_cal(this.payload_q);

    this.head_err = (this.header != 16'h55d5);
    this.tail_err = (this.tailer != tailer);
    this.payload_mode = this.pkt_len <= 2  ? 2'd0 :
                        this.pkt_len <  22 ? 2'd1 :
                        this.pkt_len <  603? 2'd2 :
                                             2'd3 ;

endfunction: unpack

function spt_transaction spt_transaction::spt_proc();
    spt_transaction to = new();
    to.pkt_data = new[this.pkt_len-2];
    foreach(this.pkt_data[i]) begin
        if(i != 0 && i != this.pkt_data.size() - 1)begin
            to.pkt_data[i-1] = {this.pkt_data[i][7:0], this.pkt_data[i][15:8]};
            //$display(to.pkt_data[i-1]);
        end
    end
    //to.unpack();
    
    return to;
endfunction: spt_proc


`endif
